题目分析
本题是周赛的第四题,题意比较简单,和Leetcode300题非常相似,主要考察小伙伴们一题多解的能力,这个也是我写这些博客的目的。在很多题目里,我都会写出某个题的多种解法,如果第300题,小伙伴们只掌握了单调栈+二分的做法,那么本题是无法求解的。小伙伴们可以去看一下最长递增子序列的其他解法,然后试着写出本题。
树状数组
这个题目的思路就不做过多介绍了,可以看一下最长递增子序列的分析。
这里主要介绍一下本题和第300题的区别,第300题不限制递增序列中,相邻两个元素的距离。因此使用树状数组和线段树查找时,直接找从0到x - 1的元素最大值即可。
本题的难度大的原因在于,要寻找从x - k到x - 1的元素最大值。因为本题的元素从1开始,如果k > x,则寻找从1开始的元素最大值。,即query(max(1, x - k), x - 1)。
本题使用树状数组的难点在于,树状数组善于计算从0到x的统计意义(如求和、最大值等),如果从m到x的求和,可以使用0到x的求和减去0到m-1的求和。但是最大值不可以这么做。
因此如何使用树状数组求m到x的最大值,即query(lowIdx, highIdx)如何实现是我们需要学习的。
首先看一下树状数组中的tree,其实就是某些原始数组arr的统计表示。我们当然可以重新new一个数组表示原始数组。我默认看到这里的小伙伴是学习过树状数组的,下面直接讲专业术语。
如果highIdx - lowBit(highIdx) >= lowIdx,说明tree[highIdx]中的原始在查找的区间内。否则tree[highIdx]表示的元素中,有部分元素小于lowIdx区间,如果这部分元素决定着tree[highIdx]的最大值,那么查询到的结果是不正确的。
如果highIdx - lowBit(highIdx) >= lowIdx,则highIdx可以更新到highIdx - lowBit(highIdx)。否则只能查询原始数组中highIdx的值arr[highIdx],然后将highIdx - 1,继续查询。
一共有log(n)位,每一次-1后,低位都是1,因此又需要log(n)的时间去减lowBit。因此算法的时间复杂度为$ O(nlog^2(n)) $,空间复杂度为O(n),n为数组的最大值。
1 | class Solution { |
线段树
本题的最优解是线段树,线段树本身就提供查询某一段区间的统计意义,因此直接看代码即可。
对线段树不熟悉的小伙伴,可以参考线段树
算法的时间复杂度为$ O(nlog(n)) $,空间复杂度为O(n)。
1 | class Solution { |
刷题总结
树状数组和线段树经常出现在一些比赛中,难度不大,有固定的代码模板。不推荐小伙伴们背模板,但是每天写一遍,一星期就可以熟练写出来线段树的代码了。